/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.util.bean;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.elsfs.cloud.common.util.lang.ArrayUtils;
import org.elsfs.cloud.common.util.lang.CharUtil;
import org.elsfs.cloud.common.util.text.StringFormatter;

/**
 * Bean路径表达式，用于获取多层嵌套Bean中的字段值或Bean对象
 *
 * <p>根据给定的表达式，查找Bean中对应的属性值对象。 表达式分为两种：
 *
 * <ol>
 *   <li>.表达式，可以获取Bean对象中的属性（字段）值或者Map中key对应的值
 *   <li>[]表达式，可以获取集合等对象中对应index的值
 * </ol>
 *
 * <p>表达式示例：
 *
 * <pre>
 * person
 * person.name
 * persons[3]
 * person.friends[5].name
 * ['person']['friends'][5]['name']
 * </pre>
 */
public class BeanPath {
  /** 表达式边界符号数组 */
  private static final char[] EXP_CHARS = {
    CharUtil.DOT, CharUtil.BRACKET_START, CharUtil.BRACKET_END
  };

  private boolean isStartWith = false;
  protected List<String> patternParts;

  public BeanPath(String expression) {
    init(expression);
  }

  public static BeanPath create(String expression) {
    return new BeanPath(expression);
  }

  private void init(final String expression) {
    final List<String> localPatternParts = new ArrayList<>();
    final int length = expression.length();

    final StringBuilder builder = new StringBuilder();
    char c;
    boolean isNumStart = false; // 下标标识符开始
    boolean isInWrap = false; // 标识是否在引号内
    for (int i = 0; i < length; i++) {
      c = expression.charAt(i);
      if (0 == i && '$' == c) {
        // 忽略开头的$符，表示当前对象
        isStartWith = true;
        continue;
      }

      if ('\'' == c) {
        // 结束
        isInWrap = (!isInWrap);
        continue;
      }

      if (!isInWrap && ArrayUtils.contains(EXP_CHARS, c)) {
        // 处理边界符号
        if (CharUtil.BRACKET_END == c) {
          // 中括号（数字下标）结束
          if (!isNumStart) {
            throw new IllegalArgumentException(
                StringFormatter.format(
                    "Bad expression '{}':{}, we find ']' but no '[' !", expression, i));
          }
          isNumStart = false;
          // 中括号结束加入下标
        } else {
          if (isNumStart) {
            // 非结束中括号情况下发现起始中括号报错（中括号未关闭）
            throw new IllegalArgumentException(
                StringFormatter.format(
                    "Bad expression '{}':{}, we find '[' but no ']' !", expression, i));
          } else if (CharUtil.BRACKET_START == c) {
            // 数字下标开始
            isNumStart = true;
          }
          // 每一个边界符之前的表达式是一个完整的KEY，开始处理KEY
        }
        if (!builder.isEmpty()) {
          localPatternParts.add(builder.toString());
        }
        builder.setLength(0);
      } else {
        // 非边界符号，追加字符
        builder.append(c);
      }
    }

    // 末尾边界符检查
    if (isNumStart) {
      throw new IllegalArgumentException(
          StringFormatter.format(
              "Bad expression '{}':{}, we find '[' but no ']' !", expression, length - 1));
    } else {
      if (!builder.isEmpty()) {
        localPatternParts.add(builder.toString());
      }
    }

    // 不可变List
    this.patternParts = Collections.unmodifiableList(localPatternParts);
  }

  /**
   * 根据Bean路径表达式获取对象的值
   *
   * @param root 根对象
   * @return 对应的值，如果路径无效返回null
   */
  public Object get(Object root) {

    Object current = root;

    for (String token : patternParts) {
      if (current == null) {
        return null;
      }

      // Map 获取
      if (current instanceof Map) {
        current = ((Map<?, ?>) current).get(token);
        continue;
      }

      // List / 数组获取
      if (current instanceof List || current.getClass().isArray()) {
        try {
          int index = Integer.parseInt(token);
          if (current instanceof List<?> list) {
            current = index >= 0 && index < list.size() ? list.get(index) : null;
          } else {
            Object[] arr = (Object[]) current;
            current = index >= 0 && index < arr.length ? arr[index] : null;
          }
        } catch (NumberFormatException e) {
          return null;
        }
        continue;
      }

      // Bean 获取
      try {
        Field field = findField(current.getClass(), token);
        if (field == null) {
          return null;
        }
        field.setAccessible(true);
        current = field.get(current);
      } catch (IllegalAccessException e) {
        return null;
      }
    }

    return current;
  }

  /**
   * 将表达式拆分为token
   *
   * <p>支持: person.friends[3].name ['person']['friends'][5]['name']
   *
   * @param expression 表达式
   * @return token数组
   */
  private static String[] tokenize(String expression) {
    expression =
        expression
            .replaceAll("\\['", ".")
            .replaceAll("'\\]", "")
            .replaceAll("\\[", ".")
            .replaceAll("]", "");
    return expression.split("\\.");
  }

  /** 查找字段，包括父类 */
  private static Field findField(Class<?> clazz, String fieldName) {
    while (clazz != null) {
      try {
        return clazz.getDeclaredField(fieldName);
      } catch (NoSuchFieldException e) {
        clazz = clazz.getSuperclass();
      }
    }
    return null;
  }
}
